#include "ndata.h"
#include "dvb.h"
#include <string.h>
#include <stdlib.h>

DWORD Ndata::M_count = 0;

void Ndata::SetNull(void)
{
    NTYPE(m_bBuffer) = NULL_TYPE;
    m_len = NLENGTH(m_bBuffer) = 4;
}

void Ndata::SetDataBody(WORD type, const void* body, DWORD bodySize)
{
    _EnsureSize(4 + bodySize, 8);
    NTYPE(m_bBuffer) = type;
    memcpy(m_bBuffer + 4, body, bodySize);
    m_len = NLENGTH(m_bBuffer) = (WORD)(4 + bodySize);
}

void Ndata::SetWord(WORD w)
{
    SetDataBody(W_TYPE, &w, sizeof(WORD));
}

void Ndata::SetDword(DWORD dw)
{
    SetDataBody(DW_TYPE, &dw, sizeof(DWORD));
}

void Ndata::SetDvb(DVB dvb)
{
    SetDataBody(DVB_TYPE, &dvb, sizeof(DVB));
}

WORD Ndata::DataType(void)
{
    return(NTYPE(m_bBuffer));
}

WORD Ndata::DataSize(void)
{
    return(NLENGTH(m_bBuffer) - 4);
}

const void* Ndata::DataBody(void)
{
    return(m_bBuffer + 4);
}

static WORD GetTypeWord(WORD type, const BYTE dataBody[])
{
    WORD w = 0;
    char firstChar;
    switch (type) {
        case W_TYPE:
        case DVB_TYPE:
        case DW_TYPE:
            w = *(WORD*)dataBody;
            break;
        case STR_TYPE:
            firstChar = dataBody[0];
            if ((firstChar >= '0' && firstChar <= '9') || firstChar == '-') {
                w = (WORD)atol((const char*)dataBody);
            }
            break;
        default:
            break;
    }
    return(w);
}

DWORD GetTypeDword(WORD type, const BYTE dataBody[])
{
    DWORD dw = 0;
    char firstChar;
    switch (type) {
        case W_TYPE:
            dw = *(WORD*)dataBody;
            break;
        case DVB_TYPE:
        case DW_TYPE:
            dw = *(DWORD*)(dataBody);
            break;
        case STR_TYPE:
            firstChar = dataBody[0];
            if ((firstChar >= '0' && firstChar <= '9') || firstChar == '-') {
                dw = (DWORD)atol((const char*)dataBody);
            }
            break;
        default:
            break;
    }
    return(dw);
}

long GetTypeInt(WORD type, const BYTE dataBody[])
{
    long iv = 0;
    char firstChar;
    switch (type) {
        case W_TYPE:
            iv = *(WORD*)dataBody;
            break;
        case DVB_TYPE:
            iv = Dvb2Int((*(DVB*)dataBody));
            break;
        case DW_TYPE:
            iv = *(DWORD*)(dataBody);
            break;
        case STR_TYPE:
            firstChar = dataBody[0];
            if ((firstChar >= '0' && firstChar <= '9') || firstChar == '-') {
                iv = atol((const char*)dataBody);
            }
            break;
        default:
            break;
    }
    return(iv);
}

double GetTypeFloat(WORD type, const BYTE dataBody[])
{
    double fv = 0;
    char firstChar;
    switch (type) {
        case W_TYPE:
            fv = *(WORD*)dataBody;
            break;
        case DVB_TYPE:
            fv = Dvb2Float((*(DVB*)dataBody));
            break;
        case DW_TYPE:
            fv = *(DWORD*)(dataBody);
            break;
        case STR_TYPE:
            firstChar = dataBody[0];
            if ((firstChar >= '0' && firstChar <= '9') || firstChar == '-') {
                fv = atof((const char*)dataBody);
            }
            break;
        default:
            break;
    }
    return(fv);
}

const char* GetTypeString(WORD type, const BYTE dataBody[])
{
    const char* str = "";
    switch (type) {
        case STR_TYPE:
            str = (const char*)dataBody;
            break;
        default:
            break;
    }
    return(str);
}

const DVB* GetTypeDvbArray(WORD type, const BYTE dataBody[])
{
    static const DVB EMPTY_DVBA[] = { EODVB };
    const DVB* dva = EMPTY_DVBA;
    switch (type) {
        case DVBA_TYPE:
            dva = (const DVB*)dataBody;
            break;
        default:
            break;
    }
    return(dva);
}

WORD Ndata::GetWord(void)
{
    return(GetTypeWord(NTYPE(m_bBuffer), m_bBuffer + 4));
}

DWORD Ndata::GetDword(void)
{
    return(GetTypeDword(NTYPE(m_bBuffer), m_bBuffer + 4));
}

long Ndata::GetInt(void)
{
    return(GetTypeInt(NTYPE(m_bBuffer), m_bBuffer + 4));
}

double Ndata::GetFloat(void)
{
    return(GetTypeFloat(NTYPE(m_bBuffer), m_bBuffer + 4));
}

const char* Ndata::GetString(void)
{
    return(GetTypeString(NTYPE(m_bBuffer), m_bBuffer + 4));
}

const DVB* Ndata::GetDvbArray(void)
{
    return(GetTypeDvbArray(NTYPE(m_bBuffer), m_bBuffer + 4));
}

void NCollection::AppendWord(WORD w)
{
    AppendDataBody(W_TYPE, &w, sizeof(WORD));
}

void NCollection::AppendDword(DWORD dw)
{
    AppendDataBody(DW_TYPE, &dw, sizeof(DWORD));
}

void NCollection::AppendDvb(DVB dvb)
{
    AppendDataBody(DVB_TYPE, &dvb, sizeof(DVB));
}

void NCollection::AppendMember(Ndata* paMember)
{
    AppendDataBody(paMember->DataType(), paMember->DataBody(), paMember->DataSize());
}

NList::NList(void) : NCollection(32)
{
    NTYPE(m_bBuffer) = LIST_TYPE;
    m_len = NLENGTH(m_bBuffer) = 4;
}

NList::NList(DWORD initSize) : NCollection(initSize)
{
    NTYPE(m_bBuffer) = LIST_TYPE;
    m_len = NLENGTH(m_bBuffer) = 4;
}

void NList::Reset(void)
{
    NTYPE(m_bBuffer) = LIST_TYPE;
    m_len = NLENGTH(m_bBuffer) = 4;
}

void NList::AppendDataBody(WORD type, const void* body, DWORD bodySize)
{
    if (m_len == 4) {
        _EnsureSize(8 + bodySize);
        BYTE* firstMemberPtr = m_bBuffer + 4;
        NTYPE(firstMemberPtr) = type;
        NLENGTH(firstMemberPtr) = (WORD)(4 + bodySize);
        memcpy(firstMemberPtr + 4, body, bodySize);
        m_len = NLENGTH(m_bBuffer) = (WORD)(8 + bodySize);
    }
    else {
        BYTE* firstMemberPtr = m_bBuffer + 4;
        WORD memberType = NTYPE(firstMemberPtr);
        WORD memberDataSize = NLENGTH(firstMemberPtr) - 4;
        if (type == memberType && bodySize == memberDataSize) {
            _EnsureSize(m_len + bodySize);
            memcpy(m_bBuffer + m_len,  body, bodySize);
            m_len = NLENGTH(m_bBuffer) = (WORD)(m_len + bodySize);
        }
    }
}

WORD NList::Count(void)
{
    WORD totalLength = NLENGTH(m_bBuffer);
    if (totalLength == 4) {
        return(0);
    }

    WORD memberLength = NLENGTH(m_bBuffer + 4);
    WORD memberDataSize = memberLength - 4;
    return((totalLength - 8) / memberDataSize);
}

BOOL NList::GetMember(Ndata* paDstMember, WORD index)
{
    WORD totalLength = NLENGTH(m_bBuffer);
    if (totalLength == 4) {
        return(FALSE);
    }

    WORD memberLength = NLENGTH(m_bBuffer + 4);
    WORD memberDataSize = memberLength - 4;
    WORD getPosi = 8 + memberDataSize * index;
    if (getPosi < totalLength) {
        paDstMember->SetDataBody(NTYPE(m_bBuffer + 4), m_bBuffer + getPosi, memberDataSize);
        return(TRUE);
    }
    else {
        return(FALSE);
    }
}

NPack::NPack(void) : NCollection(16)
{
    NTYPE(m_bBuffer) = PACK_TYPE;
    m_len = NLENGTH(m_bBuffer) = 4;
}

NPack::NPack(DWORD initSize) : NCollection(initSize)
{
    NTYPE(m_bBuffer) = PACK_TYPE;
    m_len = NLENGTH(m_bBuffer) = 4;
}

void NPack::Reset(void)
{
    NTYPE(m_bBuffer) = PACK_TYPE;
    m_len = NLENGTH(m_bBuffer) = 4;
}

void NPack::AppendDataBody(WORD type, const void* body, DWORD bodySize)
{
    WORD appendLength = 4 + (WORD)bodySize;
    _EnsureSize(m_len + appendLength);

    BYTE* nextMemberPtr = m_bBuffer + m_len;
    NTYPE(nextMemberPtr) = type;
    NLENGTH(nextMemberPtr) = appendLength;
    memcpy(nextMemberPtr + 4, body, bodySize);
    m_len = NLENGTH(m_bBuffer) = (WORD)(m_len + appendLength);
}

WORD NPack::Count(void)
{
    WORD totalLength = NLENGTH(m_bBuffer);
    WORD sumLength = 4;
    BYTE* memberPtr = m_bBuffer + 4;
    WORD count = 0;
    while (sumLength < totalLength) {
        WORD memberLength = NLENGTH(memberPtr);
        sumLength += memberLength;
        memberPtr += memberLength;
        count++;
    }
    return(count);
}

const BYTE* NPack::_GetMemberPtr(WORD index)
{
    WORD totalLength = NLENGTH(m_bBuffer);
    WORD sumLength = 4;
    BYTE* memberPtr = m_bBuffer + 4;
    WORD i = 0;
    while (i < index && sumLength < totalLength) {
        i++;
        WORD nextMemberLength = NLENGTH(memberPtr);
        sumLength += nextMemberLength;
        memberPtr += nextMemberLength;
    }
    return(i == index ? m_bBuffer + sumLength : NULL);
}

BOOL NPack::GetMember(Ndata* paDstMember, WORD index)
{
    const BYTE* memberPtr = _GetMemberPtr(index);
    if (memberPtr != NULL) {
        paDstMember->ImportN(memberPtr);
        return(TRUE);
    }
    else {
        return(FALSE);
    }
}

WORD NPack::GetMemberWord(WORD index)
{
    const BYTE* memberPtr = _GetMemberPtr(index);
    if (memberPtr != NULL) {
        return(GetTypeWord(NTYPE(memberPtr), memberPtr + 4));
    }
    else {
        return(0);
    }
}

DWORD NPack::GetMemberDword(WORD index)
{
    const BYTE* memberPtr = _GetMemberPtr(index);
    if (memberPtr != NULL) {
        return(GetTypeDword(NTYPE(memberPtr), memberPtr + 4));
    }
    else {
        return(0);
    }
}

long NPack::GetMemberInt(WORD index)
{
    const BYTE* memberPtr = _GetMemberPtr(index);
    if (memberPtr != NULL) {
        return(GetTypeInt(NTYPE(memberPtr), memberPtr + 4));
    }
    else {
        return(0);
    }
}

double NPack::GetMemberFloat(WORD index)
{
    const BYTE* memberPtr = _GetMemberPtr(index);
    if (memberPtr != NULL) {
        return(GetTypeFloat(NTYPE(memberPtr), memberPtr + 4));
    }
    else {
        return(0.0);
    }
}

const char* NPack::GetMemberString(WORD index)
{
    const BYTE* memberPtr = _GetMemberPtr(index);
    if (memberPtr != NULL) {
        return(GetTypeString(NTYPE(memberPtr), memberPtr + 4));
    }
    else {
        return(0);
    }
}

const DVB* NPack::GetMemberDvbArray(WORD index)
{
    const BYTE* memberPtr = _GetMemberPtr(index);
    if (memberPtr != NULL) {
        return(GetTypeDvbArray(NTYPE(memberPtr), memberPtr + 4));
    }
    else {
        return(0);
    }
}

WORD NPack::GetWord(void)
{
    return(GetTypeWord(NTYPE(m_bBuffer + 4), m_bBuffer + 8));
}
DWORD NPack::GetDword(void)
{
    return(GetTypeDword(NTYPE(m_bBuffer + 4), m_bBuffer + 8));
}

long NPack::GetInt(void)
{
    return(GetTypeInt(NTYPE(m_bBuffer + 4), m_bBuffer + 8));
}

double NPack::GetFloat(void)
{
    return(GetTypeFloat(NTYPE(m_bBuffer + 4), m_bBuffer + 8));
}

const char* NPack::GetString(void)
{
    return(GetTypeString(NTYPE(m_bBuffer + 4), m_bBuffer + 8));
}

const DVB* NPack::GetDvbArray(void)
{
    return(GetTypeDvbArray(NTYPE(m_bBuffer + 4), m_bBuffer + 8));
}
